import json
import os
from typing import Dict, Any
from standard_check_wf import run_standard_check
from sentence_check_wf import run_sentence_check
from consistency_check_wf import check_consistency
from utils import get_deepseek_model


def comprehensive_evaluation(json_path: str = None) -> Dict[str, Any]:
    """
    综合评估工作流：调用三个检查功能，汇总结果并生成最终报告
    
    参数:
        json_path: 病历JSON文件路径
        
    返回:
        结构化的评估报告
    """
    if json_path is None:
        json_path = os.path.join(os.path.dirname(__file__), "data", "1.json")
    
    # 调用三个检查功能
    standard_result = run_standard_check(json_path)
    sentence_result = run_sentence_check(json_path)
    consistency_result = check_consistency(json_path)
    
    # 提取各类错误列表
    standard_errors = standard_result.get("基础错误列表", [])
    sentence_errors = sentence_result.get("内容不合理列表", [])
    consistency_errors = consistency_result.get("内部不一致列表", [])
    
    # 汇总所有错误并去重
    all_errors = list(set(standard_errors + sentence_errors + consistency_errors))
    
    # 获取LLM模型
    llm = get_deepseek_model()
    
    # 构造系统提示词
    system_prompt = """你是一个病历质控委员会主席。请根据以下所有检查结果，生成一份最终的病历质量评估报告。
 
 [来自standard_check_wf的字段规范检查结果]:
 {standard_errors}
 
 [来自sentence_check_wf的语义完整性评估和医学逻辑校验结果]:
 {sentence_errors}
 
 [来自consistency_check_wf的内部一致性检查结果]:
 {consistency_errors}
 
 请综合以上所有信息，对病历进行打分（满分100分），如果三份结果中存在重复的地方不进行重复扣分，扣分项有重复或相似的地方不重复扣分，输出一份结构化的 JSON 评估报告。
 
 评分细则：
 - 主诉（15分）
    **基本要求**：患者感受最痛苦的、最主要的症状或体征及其持续时间。语言精炼，术语规范，能导致第一诊断。
    - **详细规范**：
        - **初诊**：必须包含“**症状/体征 + 时间**”。时间应具体，避免“半天”等模糊词汇。
        - **复诊**：可接受格式为“**诊断名称+复诊**”或“**症状+复诊**”。
    - **扣分细则**：
        - `-15分`：**缺主诉**。
        - `-5分`：缺**主要症状或体征**。
        - `-5分`：缺**持续时间**。
        - `-2分`：仅写“**复诊**”或“**病史同前**”等过于简单描述。
        - `-3分`：使用**诊断性用语**作为主诉（初诊）。
        - `-5分`：主诉与**第一诊断无法对应**，存在逻辑问题。
        - `-2分`：内容**累赘、不精炼**（超过20字，标点符号不计入字数）。
        - `-1分`：症状描述**使用非医学术语或口语化**。
 
 - 月经史（5分）
    **基本要求**：所有育龄期女性患者（通常指15-49岁）必须记录。
    - **详细规范**：
        - **月经史**：格式为“**初潮年龄 经期天数/月经周期天数 末次月经时间**”。若有痛经、月经量异常等需注明。
        - **生育史**：特别是**近42天内**的分娩、流产（自然/人工）、异位妊娠史必须明确记录。
        - **已绝经**：应记录绝经年龄。
        - **无性生活史**：应明确记录“否认性生活史”。
    - **扣分细则**：
        - `-5分`：育龄妇女**完全无月经史记录**。
        - `-2分`：**记录不规范**（如只写“月经正常”）。
        - `-2分`：缺**末次月经**。
        - `-5分`：缺**近42天内相关生育史**。
        - `-2分`：有相关症状但未记录有无性生活史。  

 - 现病史（15分）
 **基本要求**：主诉的扩展和深化，是病历的灵魂。需逻辑清晰，能反映疾病的发展和诊疗过程。
    - **详细规范**：
        - **初诊**：
            1. **起病情况**：发病时间、诱因、缓急。
            2. **主要症状**：部位、性质、程度、持续时间、加重/缓解因素。
            3. **伴随症状**：与诊断相关的阳性和**重要的阴性症状**（用于鉴别诊断）。
            4. **诊疗经过**：发病后至本院就诊前的治疗（自服药、外院诊治）及效果。
            5. **一般情况**：发病后的精神、食欲、睡眠、体重变化等。
        - **复诊**：
            1. 重点描述**上次诊疗后的病情变化**（好转、加重、无变化）。
            2. 详细记录**治疗反应**（药物疗效、不良反应）。
            3. 出现的新症状或问题。
    - **扣分细则**：
        - `-15分`：**缺现病史**。
        - `-5分`：缺对**主要症状的展开描述**（仅重复主诉）。
        - `-3分`：缺**诊疗经过**。
        - `-2分/项`：缺**重要的、有鉴别意义的阴性症状**。
        - `-5分`：复诊病历无**治疗后变化及反应**的描述。
        - `-5分`：复诊病历仅写"**病情同前**"等无效描述。
        - `-5分`：现病史内容与**主诉、体格检查或诊断出现矛盾**。
        - `-2分`：**叙述混乱**，层次不清。
        - `-2分`：时间顺序**颠三倒四**。

 - 过敏史（5分）
    **基本要求**：必须明确记录，是医疗安全的核心。
    - **详细规范**：
        - 有过敏史：写明**具体过敏物质**（如"青霉素"、"海鲜"）及**过敏反应表现**（如"皮疹"、"休克"）。
        - 无过敏史：必须明确书写"**未发现药物及食物过敏史**"或"**无过敏史**"。
        - 不详：患者无法确认时，应记录"**过敏史不详**"。
    - **扣分细则**：
        - `-5分`：**完全无过敏史记录**（空白）。
        - `-2分`：使用"-"、"/"、"\"、"无"等非规范缩写。
        - `-1分`：记录为"**自诉无**"但未由医生确认。

 - 既往史（5分）
    **基本要求**：记录与本次疾病诊断、治疗相关的既往健康状况和疾病史。
    - **详细规范**：
        - 包括高血压、糖尿病、心脏病、手术史、外伤史、输血史、传染病史等。
        - 如无相关特殊病史，应规范书写"**既往体健**"或"**否认高血压、糖尿病、心脏病等慢性病史**"。
    - **扣分细则**：
        - `-5分/项`：**漏填重要相关既往史**（如本次诊断肺炎，但未记录既往有 COPD 史）。
        - `-2分`：使用"**-**"、"**无**"等符号代替文字。
        - `-2分`：既往史与现病史、诊断或长期医嘱内容**前后矛盾**。
 
 - 个人史（5分）
    **基本要求**：记录与发病相关的个人生活习惯、职业、环境及流行病学接触史。
    - **详细规范**：
        - **个人史**：吸烟史（量、年）、饮酒史（量、年、种类）、职业暴露史、治游史（如相关）。
        - **流行病学史**：在传染病高发期或疫情期间，必须记录相关的旅行史、接触史、聚集史。
    - **扣分细则**：
        - `-2-5分`：根据病情，**漏填相关个人史项**（如呼吸系统疾病未记录吸烟史）。
        - `-5分`：在要求时段内，**缺流行病学史**记录。
 
 - 体格检查（20分）
    **基本要求**：系统、全面、有重点，与现病史和诊断相呼应。
    - **详细规范**：
        - **生命体征**：T、P、R、BP 必须记录。
        - **一般情况**：神志、体位、步态。
        - **重点部位**：根据主诉和现病史，对相关系统进行重点检查，记录阳性体征和有鉴别意义的阴性体征。
        - **专科检查**：详细记录与本病相关的专科情况。
        - **复诊病历**：必须记录**阳性体征的变化情况**。
    - **扣分细则**：
        - `-20分`：**无体格检查记录**。
        - `-2分/项`：缺**必要生命体征**。
        - `-5分`：缺**必要的专科检查或重点系统检查**。
        - `-3分/处`：使用"**部位+未见异常**"或"**部位（-）**"等不规范描述。
        - `-5分`：检查结果与病史、诊断**明显矛盾**。
 
 - 辅助检查（5分）
    **基本要求**：记录与本次诊疗相关的、有价值的院内/外检查结果。
    - **详细规范**：
        - 应记录**检查机构、检查项目、日期和关键结果**。
        - 对异常结果应有分析和引用。
        - 外院检查应予以注明。
    - **扣分细则**：
        - `-3分`：**有重要检查但病历中完全未记录**。
        - `-2分`：只写"见报告单"而无关键结果摘要。
        - `-2分`：缺异常结果的描述。
        - `-2分`：明显**抄错数据或张冠李戴**。
 
 - 诊断（10分）
    **基本要求**：准确、规范、主次分明，有充分的诊断依据。
    - **详细规范**：
        - 使用**规范的疾病名称**，以ICD编码为准。
        - 按"**主要诊断、次要诊断、伴随疾病**"的顺序排列。
        - 诊断应能在现病史、体格检查和辅助检查中找到**支持依据**。
        - 如诊断不明，应写"**拟诊/待查**"，并列出可能性最大的诊断。
    - **扣分细则**：
        - `-10分`：**无诊断**。
        - `-3分/个`：**漏诊断**（特别是重要合并症）。
        - `-3分`：诊断**无依据支持**。
        - `-2分`：仅以**症状或体征**代替诊断（如只写"腹痛待查"）。（应写为"腹痛原因待查：急性阑尾炎？"）
        - `-1分/处`：诊断名称**不规范**。
        - `-2分`：诊断**主次排列颠倒**。
 
 - 医嘱（5分）
    **基本要求**：清晰、具体、具有可操作性，包含治疗和病情指导。
    - **详细规范**：
        - 包括药物治疗（药名、剂量、用法、疗程）、非药物治疗、健康教育、病情观察要点、复诊时间等。
        - 应与诊断和病情严重程度相符。
    - **扣分细则**：
        - `-5分`：**无处理意见记录**。
        - `-2分`：处理意见与诊断、病情**明显不符**。
        - `-5分`：开具**禁忌药物**。
        - `-2分`：**无病情嘱托**（如注意事项、复诊时间）。
        - `-2分`：医嘱**过于笼统**（如只写"对症治疗"）。
 
 - 签名（10分）
   **基本要求**：病历要有医生规范签名。
   - **扣分细则**：无医生签名扣10分。

 备注：
 - 总分为100分;
 
 输出格式示例（请严格返回 JSON，可包含中文字段）：
{{
    "总体评分": 80,
    "评分细则": {{
        "字段规范性扣分": 5,
        "内容完整性扣分": 5,
        "医学逻辑性扣分": 5,
        "内容一致性扣分": 5
    }},
    "主要问题": [
        "问题1描述",
        "问题2描述"
    ],
    "改进建议": [
        "建议1",
        "建议2"
    ]
}}"""
    # 构造用户提示词
    user_prompt = system_prompt.format(
        standard_errors=json.dumps(standard_errors, ensure_ascii=False, indent=2),
        sentence_errors=json.dumps(sentence_errors, ensure_ascii=False, indent=2),
        consistency_errors=json.dumps(consistency_errors, ensure_ascii=False, indent=2)
    )
    
    # 调用模型生成综合评估报告
    messages = [
        {"role": "system", "content": "你是一个病历质控委员会主席"},
        {"role": "user", "content": user_prompt}
    ]
    
    response = llm.invoke(messages)
    content = response.content.strip()
    
    # 尝试解析LLM返回的JSON
    try:
        # 清理常见的 Markdown 代码块包装（比如 ```json ... ``` 或 ``` ... ```）
        stripped = content.strip()
        if stripped.startswith("```"):
            # 跳过开头的 ```... 行（可能带语言标识），并移除末尾的 ```
            first_newline = stripped.find("\n")
            if first_newline != -1:
                stripped = stripped[first_newline+1:].rstrip()
            if stripped.endswith("```"):
                stripped = stripped[:-3].strip()
        
        # 1) 尝试直接解析
        try:
            report = json.loads(stripped)
        except json.JSONDecodeError:
            # 2) 如果直接解析失败，使用 JSONDecoder.raw_decode 从文本中搜索第一个合法 JSON 对象/数组
            decoder = json.JSONDecoder()
            s = stripped
            report = None
            # 从文本中找到第一个可能的 JSON 起始字符并尝试解析
            for i, ch in enumerate(s):
                if ch in ('{', '['):
                    try:
                        obj, end = decoder.raw_decode(s[i:])
                        report = obj
                        break
                    except json.JSONDecodeError:
                        continue
            if report is None:
                # 再尝试原始 content（不去除代码块标签）的搜索，以防 LLM 在其他位置包裹 JSON
                s2 = content
                for i, ch in enumerate(s2):
                    if ch in ('{', '['):
                        try:
                            obj, end = decoder.raw_decode(s2[i:])
                            report = obj
                            break
                        except json.JSONDecodeError:
                            continue
            if report is None:
                # 最终仍然解析失败，抛出以进入 except 分支
                raise json.JSONDecodeError("无法从 LLM 响应中提取 JSON", content, 0)
    except (json.JSONDecodeError, Exception):
        # 如果解析失败，返回基本结构并保留原始响应文本以便排查
        report = {
            "总体评分": 0,
            "评分细则": {
                "字段规范性得分": 0,
                "内容完整性得分": 0,
                "医学逻辑性得分": 0,
                "一致性得分": 0
            },
            "主要问题": all_errors,
            "改进建议": ["无法生成具体建议"],
            "原始响应": content
        }
    
    # 添加原始检查结果到报告中
    report["各阶段检查结果"] = {
        "字段规范检查": standard_result,
        "语义完整性检查": sentence_result,
        "内部一致性检查": consistency_result
    }
    
    # 将评估报告保存到 ./output，文件名为 <输入文件名(无扩展)>result.json
    try:
        # 固定输出目录为项目根下的 output 文件夹
        output_dir = os.path.join(os.path.dirname(__file__), "output")
        output_dir = os.path.abspath(output_dir)
        os.makedirs(output_dir, exist_ok=True)
        base_name = os.path.splitext(os.path.basename(json_path))[0] if json_path else "result"
        out_path = os.path.join(output_dir, f"{base_name}_result.json")
        with open(out_path, "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=2)
    except Exception as e:
        # 保存失败不影响返回，打印错误以便排查
        print(f"保存评估结果失败: {e}")
    
    return report


if __name__ == "__main__":
    # 测试综合评估功能
    test_data_path = os.path.join(os.path.dirname(__file__), "data", "1.json")
    
    if not os.path.exists(test_data_path):
        print(f"错误: 测试数据文件未找到: {test_data_path}")
    else:
        result = comprehensive_evaluation(test_data_path)
        print("综合评估报告:")
        print(json.dumps(result, ensure_ascii=False, indent=2))